package business.test;

import business.server.RaftKVServer;

import java.net.HttpURLConnection;
import java.net.URL;
import java.util.*;

/**
 * Raft集群测试程序
 * 演示节点下线、日志同步、重新加入集群的过程
 */
public class RaftClusterDemo {
    private List<RaftKVServer> servers;
    private Map<String, String> peerAddresses;
    private List<String> nodeIds;
    
    public RaftClusterDemo() {
        // 初始化节点信息
        nodeIds = Arrays.asList("node1", "node2", "node3");
        peerAddresses = new HashMap<>();
        peerAddresses.put("node1", "localhost:8081");
        peerAddresses.put("node2", "localhost:8082");
        peerAddresses.put("node3", "localhost:8083");
        servers = new ArrayList<>();
    }
    
    public void startCluster() throws Exception {
        System.out.println("正在启动Raft集群...");
        // 创建并启动服务器
        for (int i = 0; i < nodeIds.size(); i++) {
            String nodeId = nodeIds.get(i);
            int port = 8081 + i;
            List<String> peers = new ArrayList<>(nodeIds);
            peers.remove(nodeId); // 移除自身
            
            System.out.println("启动节点 " + nodeId + ":");
            System.out.println("  - 端口: " + port);
            System.out.println("  - 地址: " + peerAddresses.get(nodeId));
            System.out.println("  - 集群节点: " + peers);
            
            RaftKVServer server = new RaftKVServer(nodeId, port, peers, peerAddresses);
            servers.add(server);
            server.start();
        }
        
        // 等待集群初始化
        System.out.println("\n等待集群初始化...");
        Thread.sleep(5000);
        
        // 打印集群状态
        printClusterStatus();
    }
    
    private void printClusterStatus() throws Exception {
        System.out.println("\n集群状态:");
        String leaderAddress = findLeaderAddress();
        
        for (RaftKVServer server : servers) {
            String nodeId = server.getNodeId();
            String address = server.getAddress();
            String status = address.equals(leaderAddress) ? "Leader" : "Follower";
            
            System.out.println("节点 " + nodeId + ":");
            System.out.println("  - 地址: " + address);
            System.out.println("  - 状态: " + status);
            
            // 获取节点详细状态
            String nodeStatus = getNodeStatus(address);
            if (nodeStatus != null) {
                System.out.println("  - 详细信息: " + nodeStatus);
            }
        }
    }
    
    private String getNodeStatus(String address) {
        try {
            URL url = new URL("http://" + address + "/status");
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("GET");
            
            if (conn.getResponseCode() == 200) {
                try (Scanner scanner = new Scanner(conn.getInputStream())) {
                    return scanner.hasNext() ? scanner.nextLine() : null;
                }
            }
        } catch (Exception e) {
            System.err.println("获取节点状态失败: " + e.getMessage());
        }
        return null;
    }
    
    public void stopCluster() {
        System.out.println("\n关闭集群...");
        for (RaftKVServer server : servers) {
            String nodeId = server.getNodeId();
            String address = server.getAddress();
            System.out.println("关闭节点 " + nodeId + " (" + address + ")");
            server.stop();
        }
    }
    
    public void runTest() throws Exception {
        try {
            // 1. 写入初始数据
            System.out.println("\nStep 1: 写入初始数据");
            putValue("key1", "value1");
            putValue("key2", "value2");
            putValue("key3", "value3");
            
            // 验证数据
            System.out.println("\n验证初始数据:");
            checkValue("key1", "value1");
            checkValue("key2", "value2");
            checkValue("key3", "value3");
            
            // 打印当前集群状态
            printClusterStatus();
            
            // 2. 下线一个节点
            System.out.println("\nStep 2: 下线一个节点");
            String leaderAddress = findLeaderAddress();
            RaftKVServer nodeToStop = null;
            for (RaftKVServer server : servers) {
                if (!server.getAddress().equals(leaderAddress)) {
                    nodeToStop = server;
                    break;
                }
            }
            
            String stoppedNodeId = nodeToStop.getNodeId();
            String stoppedAddress = nodeToStop.getAddress();
            System.out.println("下线节点: " + stoppedNodeId + " (" + stoppedAddress + ")");
            nodeToStop.stop();
            servers.remove(nodeToStop);
            
            // 等待集群重新选举
            System.out.println("等待集群重新选举...");
            Thread.sleep(5000);
            printClusterStatus();
            
            // 3. 继续写入新数据
            System.out.println("\nStep 3: 继续写入数据");
            putValue("key4", "value4");
            putValue("key5", "value5");
            
            // 4. 重启已下线节点
            System.out.println("\nStep 4: 重启节点");
            List<String> peers = new ArrayList<>(nodeIds);
            peers.remove(stoppedNodeId);
            
            System.out.println("重启节点 " + stoppedNodeId + ":");
            System.out.println("  - 地址: " + stoppedAddress);
            System.out.println("  - 集群节点: " + peers);
            
            RaftKVServer restartedServer = new RaftKVServer(stoppedNodeId, 
                Integer.parseInt(peerAddresses.get(stoppedNodeId).split(":")[1]),
                peers, peerAddresses);
            restartedServer.start();
            servers.add(restartedServer);
            
            // 等待节点同步
            System.out.println("等待节点同步...");
            Thread.sleep(5000);
            printClusterStatus();
            
            // 5. 验证数据同步
            System.out.println("\nStep 5: 验证数据同步");
            System.out.println("从重启的节点读取数据 (" + stoppedAddress + "):");
            checkValueFromNode(stoppedNodeId, "key1", "value1");
            checkValueFromNode(stoppedNodeId, "key2", "value2");
            checkValueFromNode(stoppedNodeId, "key3", "value3");
            checkValueFromNode(stoppedNodeId, "key4", "value4");
            checkValueFromNode(stoppedNodeId, "key5", "value5");
            
            System.out.println("\n测试完成：节点成功重新加入集群并同步数据");
            
        } catch (Exception e) {
            System.err.println("测试过程中发生错误: " + e.getMessage());
            e.printStackTrace();
            throw e;
        }
    }
    
    private void putValue(String key, String value) throws Exception {
        String leaderAddress = findLeaderAddress();
        if (leaderAddress == null) {
            throw new RuntimeException("未找到Leader节点");
        }
        
        URL url = new URL("http://" + leaderAddress + "/kv/" + key);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setRequestMethod("PUT");
        conn.setDoOutput(true);
        conn.getOutputStream().write(value.getBytes());
        
        int responseCode = conn.getResponseCode();
        if (responseCode != 200) {
            throw new RuntimeException("写入失败，状态码: " + responseCode);
        }
        
        System.out.println("写入成功: " + key + " = " + value);
        // 等待数据同步
        Thread.sleep(1000);
    }
    
    private String getValue(String key) throws Exception {
        // 从任意节点读取
        String address = peerAddresses.get(nodeIds.get(0));
        return getValueFromAddress(address, key);
    }
    
    private String getValueFromNode(String nodeId, String key) throws Exception {
        String address = peerAddresses.get(nodeId);
        return getValueFromAddress(address, key);
    }
    
    private String getValueFromAddress(String address, String key) throws Exception {
        URL url = new URL("http://" + address + "/kv/" + key);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setRequestMethod("GET");
        
        if (conn.getResponseCode() != 200) {
            return null;
        }
        
        try (Scanner scanner = new Scanner(conn.getInputStream())) {
            return scanner.hasNext() ? scanner.nextLine() : null;
        }
    }
    
    private void checkValue(String key, String expectedValue) throws Exception {
        String actualValue = getValue(key);
        if (expectedValue.equals(actualValue)) {
            System.out.println("验证成功: " + key + " = " + actualValue);
        } else {
            System.err.println("验证失败: " + key + " 期望值=" + expectedValue + ", 实际值=" + actualValue);
        }
    }
    
    private void checkValueFromNode(String nodeId, String key, String expectedValue) throws Exception {
        String actualValue = getValueFromNode(nodeId, key);
        if (expectedValue.equals(actualValue)) {
            System.out.println("节点 " + nodeId + " 验证成功: " + key + " = " + actualValue);
        } else {
            System.err.println("节点 " + nodeId + " 验证失败: " + key + " 期望值=" + expectedValue + ", 实际值=" + actualValue);
        }
    }
    
    private String findLeaderAddress() throws Exception {
        for (String nodeId : nodeIds) {
            String address = peerAddresses.get(nodeId);
            URL url = new URL("http://" + address + "/status");
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("GET");
            
            if (conn.getResponseCode() == 200) {
                try (Scanner scanner = new Scanner(conn.getInputStream())) {
                    String response = scanner.hasNext() ? scanner.nextLine() : "";
                    if (response.contains("\"state\":\"LEADER\"")) {
                        return address;
                    }
                }
            }
        }
        return null;
    }
    
    public static void main(String[] args) {
        RaftClusterDemo demo = new RaftClusterDemo();
        try {
            // 启动集群
            demo.startCluster();
            
            // 运行测试
            demo.runTest();
            
        } catch (Exception e) {
            System.err.println("测试失败: " + e.getMessage());
            e.printStackTrace();
        } finally {
            // 关闭集群
            demo.stopCluster();
        }
    }
} 